<template>
  <el-form-item
    v-bind="$attrs"
    :label-width="labelWidth"
  >
    <template
      v-slot:label
      v-if="labelPosition === 'none'"
    >
      <span />
    </template>
    <component
      :is="tag"
      v-model="v"
      v-on="$listeners"
      v-bind="definition"
      :fields="fields"
    >
      <template v-if="options">
        <component
          v-for="(option) in options"
          :is="option.tag"
          v-bind="option"
          :key="option.value"
        >
          <template v-if="option.text">
            {{ option.text }}
          </template>
        </component>
      </template>
      <ed-field-factory-button
        v-else-if="edit"
        v-bind="edit"
        :fields="items"
        @add="addField"
        @drop="dropField"
      />
    </component>
  </el-form-item>
</template>

<script>
/**
 * 封装了表单元素 通过定义自动渲染响应表单域
 */
import { pick } from '../../store/Form'

export default {
  beforeCreate: function () {
    this.$options.components.EdFieldGroupButton = require('./EdFieldGroupButton.vue').default
    this.$options.components.EdFieldList = require('./EdFieldList.vue').default
    this.$options.components.EdPair = require('./EdPair.vue').default
  },
  components: {
    EdAutocomplete: () => import('./EdAutocomplete'),
    EdVerify: () => import('./EdVerify'),
    EdFieldFactoryButton: () => import('./EdFieldFactoryButton'),
    EdFileUpload: () => import('./EdFileUpload'),
    EdAvatarUpload: () => import('./EdAvatarUpload')
  },
  props: {
    /**
     * 表单传入数据
     */
    // eslint-disable-next-line vue/require-prop-types
    value: {
      default: undefined
    },
    /**
     * 域信息可修改，目前只能修改fields
     */
    editable: {
      type: [Object, Boolean],
      default: null
    },
    labelPosition: {
      type: String,
      default: 'right'
    },
    labelWidth: {
      type: String,
      default: undefined
    }
  },
  data () {
    return {
      v: this.value, // 引入v是为了隔绝input事件，只通知change事件
      items: [], // 引入item是用来自定义子表单域
      block: false,
      definition: {},
      tag: '',
      options: null
    }
  },
  created () {
    const element = this.$attrs
    const props = pick(element.tag)
    const fnOption = props.option
    this.tag = props.tag || element.tag
    const definition = { ...props, ...element }
    definition.placeholder = element.placeholder || element.label || element.name
    this.definition = definition
    if (element.options) {
      this.options = (fnOption) ? element.options.map(fnOption) : element.options
    }
  },
  computed: {
    edit () {
      if (this.editable === true) return {}
      return this.editable
    },
    fields () {
      if (!this.editable) return this.definition.fields

      return [...this.definition.fields, ...this.items]
    }
  },
  watch: {
    value: {
      handler (val) { this.v = val },
      deep: true
    }
  },
  methods: {
    addField (field) {
      this.items.push(field)
    },
    dropField (field) {
      this.items.splice(this.items.indexOf(field), 1)
      const value = Object.assign({}, this.v)
      this.$delete(value, field.name)
      this.$emit('change', value)
    }
  }
}
</script>

<style scoped>

</style>
